#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import print_function, unicode_literals
import colorama
import random
import sqlite3
import sys
import time

import character_ui as ui
import nt_input

reload(sys)
sys.setdefaultencoding('utf-8')
colorama.init(autoreset=True)

DB_NAME = 'calculate.db'
LEVEL_CONFIGS = [
    {
        "id": 1,
        "name": "specific training - 1",
        "numbers": (1, ),
        "operators": ["+", "-"],
        "count": 4,
    },
    {
        "id": 2,
        "name": "specific training - 2",
        "numbers": (2, ),
        "operators": ["+", "-"],
        "count": 8,
    },
    {
        "id": 3,
        "name": "specific training - 3",
        "numbers": (3, ),
        "operators": ["+", "-"],
        "count": 12,
    },
    {
        "id": 4,
        "name": "specific training - 4",
        "numbers": (4, ),
        "operators": ["+", "-"],
        "count": 16,
    },
    {
        "id": 5,
        "name": "specific training - 5",
        "numbers": (5, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 6,
        "name": "specific training - 6",
        "numbers": (6, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 7,
        "name": "specific training - 7",
        "numbers": (7, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 8,
        "name": "specific training - 8",
        "numbers": (8, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 9,
        "name": "specific training - 9",
        "numbers": (9, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 10,
        "name": "specific training - 10",
        "numbers": (10, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 11,
        "name": "specific training - 11",
        "numbers": (11, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 12,
        "name": "specific training - 12",
        "numbers": (12, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 13,
        "name": "specific training - 13",
        "numbers": (13, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 14,
        "name": "specific training - 14",
        "numbers": (14, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 15,
        "name": "specific training - 15",
        "numbers": (15, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 16,
        "name": "specific training - 16",
        "numbers": (16, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 17,
        "name": "specific training - 17",
        "numbers": (17, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 18,
        "name": "specific training - 18",
        "numbers": (18, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 19,
        "name": "specific training - 19",
        "numbers": (19, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 20,
        "name": "specific training - 20",
        "numbers": (20, ),
        "operators": ["+", "-"],
        "count": 20,
    },
    {
        "id": 100,
        "name": "final challenge",
        "numbers": range(1, 21),
        "operators": ["+", "-"],
        "count": 100,
    },
]
SCORE_PER_PROBLEM = [
    # 用时，得分，用时显示颜色，等级
    (1, 10, colorama.Fore.YELLOW, "卓越"),
    (2.5, 6, colorama.Fore.GREEN, "优秀"),
    (5, 3, colorama.Fore.CYAN, "良好"),
    (10, 2, colorama.Fore.WHITE, "及格"),
    (20, 1, colorama.Fore.WHITE, "加油")
]
count_down = 20
total_score = 0
current_choice = 0


def init_db():
    conn = sqlite3.connect(DB_NAME)
    cur = conn.cursor()
    cur.execute('''CREATE TABLE IF NOT EXISTS rounds
                   (
                    round_id INTEGER PRIMARY KEY,
                    user_name TEXT NOT NULL,
                    config_id TEXT NOT NULL,
                    config_name TEXT NOT NULL,
                    problem_count INTEGER DEFAULT 0,
                    wrong INTEGER DEFAULT 0,
                    correct INTEGER DEFAULT 0,
                    total_score INTEGER DEFAULT 0,
                    average_score REAL DEFAULT 0,
                    total_time INTEGER DEFAULT 0,
                    average_time REAL DEFAULT 0,
                    create_time DATETIME
                    );''')
    cur.execute('''CREATE TABLE IF NOT EXISTS problems
                    (
                    round_id INTEGER NOT NULL,
                    problem TEXT NOT NULL,
                    user_answer INTEGER DEFAULT 0,
                    correct_answer INTEGER DEFAULT 0,
                    use_time REAL DEFAULT 0,
                    is_correct INTEGER DEFAULT 0,
                    create_time DATETIME
                    );''')
    conn.commit()
    conn.close()


class CalProblem:
    def __init__(self, rd, l, r, o):
        self.round = rd
        self.problem = "{}{}{}".format(l, o, r)
        self.user_answer = -1
        self.correct_answer = eval(self.problem)
        self.use_time = 0
        self.give_up = False
        self.score = 0
        self.time_color = colorama.Fore.WHITE

    def __unicode__(self):
        if self.is_correct():
            return "{}={}".format(self.problem, self.user_answer) \
                   + self.time_color + " time:{:.2f}".format(self.use_time) + colorama.Style.RESET_ALL
        else:
            return colorama.Fore.RED + "{}={} time:{:.2f}".format(
                self.problem, "" if self.give_up else self.user_answer, self.use_time) + colorama.Style.RESET_ALL

    def answer(self, a, t):
        if a == "":
            self.give_up = True
        else:
            self.user_answer = int(a)
            self.use_time = t
        self.cal_score()

    def is_correct(self):
        return not self.give_up and self.user_answer == self.correct_answer

    def get_problem(self):
        return self.problem + "="

    def cal_score(self):
        if self.is_correct():
            for t, score, c, _ in SCORE_PER_PROBLEM:
                if self.use_time <= t:
                    self.score = score
                    self.time_color = c
                    return
        self.score = 0
        self.time_color = colorama.Fore.WHITE

    def get_score(self):
        return self.score

    def print_big(self, answer=""):
        ui.print_big_character(self.get_problem() + answer)

    def save(self):
        conn = sqlite3.connect(DB_NAME)
        cur = conn.cursor()
        sql = 'INSERT INTO problems \
               (round_id, problem, user_answer, correct_answer, use_time, is_correct, create_time) VALUES \
               (%s, "%s", %s, %s, %s, %s, datetime("now", "localtime"));' % \
              (self.round.id, self.problem, self.user_answer, self.correct_answer, self.use_time, self.is_correct())
        cur.execute(sql)
        conn.commit()
        conn.close()


class CalRound:
    def __init__(self, config):
        self.id = int(time.time() * 1000)
        self.user_name = ""
        self.config = config
        self.problems = []
        self.config_id = config["id"]
        self.config_name = config["name"]
        self.count = config["count"]
        self.wrong = 0
        self.correct = 0
        self.total_score = 0
        self.average_score = 0
        self.total_time = 0
        self.average_time = 0

    def gen_problem(self):
        p = self._random_problem()
        self.problems.append(p)
        self.print_last_problem()
        self.wait_answer()
        return p

    def print_last_problem(self, answer=""):
        ui.clear_screen()
        print("第{}/{}题：".format(len(self.problems), self.count))
        self.problems[-1].print_big(answer)

    def is_over(self):
        return len(self.problems) >= self.count

    def wait_answer(self):
        if len(self.problems) < 1:
            return
        p = self.problems[-1]
        answer = ""
        start_time = time.time()
        while 1:
            c = nt_input.getch()
            if c == nt_input.ENTER:
                use_time = time.time() - start_time
                p.answer(answer, use_time)
                if p.is_correct():
                    self.correct += 1
                else:
                    self.wrong += 1
                self.total_score += p.get_score()
                self.total_time += p.use_time
                break
            elif nt_input.ZERO <= c <= nt_input.NINE:
                answer += unicode(c - nt_input.ZERO)
                self.print_last_problem(answer)
            elif c == nt_input.BACKSPACE or c == nt_input.DEL:
                if len(answer) > 0:
                    answer = answer[:-1]
                    self.print_last_problem(answer)

    def set_name(self, v):
        self.user_name = v

    def print_result(self):
        ui.clear_screen()

        # 计算得分与对错
        self.average_score = self.total_score / self.count
        self.average_time = self.total_time / self.count

        # 展示结果
        for i in range(0, self.count, 4):
            ps = self.problems[i:i+4] if i+4 < self.count else self.problems[i:]
            pf = "{:30s}"*len(ps)
            print(pf.format(*[unicode(p) for p in ps]))
        print("")
        print("总 得 分：{}".format(self.total_score))
        print("题目总数：{}".format(len(self.problems)))
        print("答对题目：{}".format(self.correct))
        print("答错题目：{}".format(self.wrong))
        print("总 用 时：{:.2f}".format(self.total_time))
        print("平均用时：{:.2f}".format(self.average_time))
        print("总 评 级：{}".format(self.get_rank()))

    def get_rank(self):
        for _, score, c, r in SCORE_PER_PROBLEM:
            if self.average_score >= score:
                return c + r
        return SCORE_PER_PROBLEM[-1][2] + SCORE_PER_PROBLEM[-1][3]

    def save(self):
        conn = sqlite3.connect(DB_NAME)
        cur = conn.cursor()

        cur.execute('INSERT INTO rounds \
               (round_id, user_name, config_id, config_name, problem_count, wrong, correct, \
               total_score, average_score, total_time, average_time, create_time) VALUES \
               (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime("now", "localtime"));',
                    (self.id, self.user_name, self.config_id, self.config_name, self.count, self.wrong, self.correct,
                     self.total_score, self.average_score, self.total_time, self.average_time))

        pl = [(p.round.id, p.problem, p.user_answer, p.correct_answer, p.use_time, p.is_correct()) for p in self.problems]
        cur.executemany('INSERT INTO problems \
              (round_id, problem, user_answer, correct_answer, use_time, is_correct, create_time) VALUES \
              (?, ?, ?, ?, ?, ?, datetime("now", "localtime"));', pl)
        conn.commit()
        conn.close()

    def _random_problem(self):
        o = random.choice(self.config["operators"])
        if o == "+":
            a = random.choice(self.config["numbers"])
            l = random.randrange(0, a+1)
            r = a - l
        else:
            l = random.choice(self.config["numbers"])
            r = random.randrange(0, l+1)
            if l < r:
                r, l = l, r
        p = CalProblem(self, l, r, o)
        return p


def print_title():
    print('************************')
    print('**     口算大通关     **')
    print('************************')


def print_problem_types():
    for i, c in enumerate(LEVEL_CONFIGS):
        if i == current_choice:
            print(colorama.Style.BRIGHT + '{}. {}'.format(i+1, c['name']))
        else:
            print('{}. {}'.format(i+1, c['name']))


def print_home():
    ui.clear_screen()
    print_title()
    print_problem_types()
    print("")
    print("[↑][↓]：选择关卡")
    print("[F1]：查看历史记录")
    print("[ESC]：退出游戏")

    while 1:
        c = nt_input.getch()
        global current_choice
        if c == nt_input.UP:
            current_choice = (current_choice - 1) % len(LEVEL_CONFIGS)
            break
        elif c == nt_input.DOWN:
            current_choice = (current_choice + 1) % len(LEVEL_CONFIGS)
            break
        elif c == nt_input.ENTER:
            current_round = CalRound(LEVEL_CONFIGS[current_choice])
            start_game(current_round)
            clear_game()
            break
        elif c == nt_input.F1:
            ui.clear_screen()
            print_history(LEVEL_CONFIGS[current_choice])
            print("[ESC]：返回")
            while 1:
                c = nt_input.getch()
                if c == nt_input.ESC:
                    break
            break
        elif c == nt_input.ESC:
            return
    print_home()


def print_history(config):
    conn = sqlite3.connect(DB_NAME)
    cur = conn.cursor()
    sql = 'SELECT user_name, config_name, total_score, total_time, create_time, correct, problem_count \
           FROM rounds WHERE config_id = "%s" ORDER BY total_score DESC LIMIT 10;' % (config['id'],)
    cur.execute(sql)
    rows = cur.fetchall()
    result = False
    print("{:^10s}{:^10s}{:^30s}{:^10s}{:^10s}{:^10s}{:^20s}".format(
        "名次", "用户", "关卡", "得分", "用时", "正确率", "时间"))
    for i, row in enumerate(rows):
        result = True
        print("{:^10s}{:^10s}{:^32s}{:^12d}{:^12.2f}{:^13.2%}{:^20s}".format(
            "第{}名".format(i+1), row[0], row[1], row[2], row[3], float(row[5])/float(row[6]), row[4]))
    conn.close()
    print("")
    return result


def start_game(r):
    while not r.is_over():
        r.gen_problem()
    r.print_result()
    print("")
    name = raw_input("输入用户名【按ENTER确定】: ")
    r.set_name(name[:10])
    r.save()


def clear_game():
    global current_choice
    current_choice = 0


if __name__ == '__main__':
    # nt_input.test_key_value()
    init_db()
    print_home()